package com.liry.security.config;

import cn.hutool.captcha.generator.MathGenerator;
import com.liry.security.util.RsaUtil;
import java.io.InputStream;
import java.security.PrivateKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * security认证配置
 *
 * @author ALI
 * @since 2023/5/29
 */
@Configuration
@EnableWebSecurity
public class SecurityWebConfig extends WebSecurityConfigurerAdapter {
    private static final String loginApi = "/loginDeal";
    private static final String usernameParameter = "username";
    private static final String passwordParameter = "pwd";

    public static PrivateKey privateKey;

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private CustomSecurityContextRepository securityContextRepository;
    @Autowired
    private CacheManager cacheManager;

    public SecurityWebConfig() throws Exception {
        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("privateKey.pem")) {
            if (is == null) {
                throw new RuntimeException("没有读取的密钥！！！");
            }
            byte[] data = new byte[2048];
            int length = is.read(data);
            String privateKeyString = new String(data, 0, length);
            privateKey = RsaUtil.getPrivateKey(privateKeyString.trim());
        }
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public CorsFilter corsFilter() {
        return new CorsFilter(corsConfigurationSource());
    }

    @Bean
    public MathGenerator mathGenerator() {
        return new MathGenerator(1);
    }

    /**
     * 自定义的customUsernamePasswordAuthenticationFilter
     * 需要同步在HttpSecurity里的配置
     */
    public CustomUsernamePasswordAuthenticationFilter customUsernamePasswordAuthenticationFilter() throws Exception {
        CustomUsernamePasswordAuthenticationFilter result = new CustomUsernamePasswordAuthenticationFilter();
        result.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(loginApi));
        result.setAuthenticationManager(this.authenticationManager());
        result.setUsernameParameter(usernameParameter);
        result.setPasswordParameter(passwordParameter);
        result.setAuthenticationSuccessHandler(new LoginSuccessHandler());
        result.setAuthenticationFailureHandler(new LoginFailureHandler());
        return result;
    }

    //    @Bean
    public PermissionService permissionService() {
        return new PermissionService(null);
    }

    /**
     * 跨域设置
     */
    private CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        // 允许cookies跨域
        config.setAllowCredentials(true);
        // 允许向该服务器提交请求的URI，* 表示全部允许，在SpringMVC中，如果设成*，会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        // 允许访问的头信息,* 表示全部
        config.addAllowedHeader("*");
        // 预检请求的缓存时间（秒），即在这个时间段里，对于相同的跨域请求不会再预检了
        config.setMaxAge(18000L);
        // 允许提交请求的方法，* 表示全部允许
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("ali")
            .password(passwordEncoder().encode("123456"))
            .roles("admin");
        auth.userDetailsService(userDetailsService);
    }
//
//    @Override
//    public void init(WebSecurity web) throws Exception {
//        HttpSecurity http = getHttp();
//        web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
//            // 因为FilterSecurityInterceptor是在其他配置完成后执行的，所以只能在这里修改
//            // 详细看 org.springframework.security.config.annotation.web.builders.WebSecurity#performBuild
//            FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
//            securityInterceptor.setAccessDecisionManager(new CustomAccessDecisionManager(cacheManager));
//            web.securityInterceptor(securityInterceptor);
//        });
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表单
        // 注意：使用了`BasicAuthenticationFilter`那么在配置`WebSecurityConfigurerAdapter`时，就不要设置`http.httpBasic()`，不然不会走我们自定义的Filter
        http
            .formLogin()
            // 登录的请求接口，对应表单的action，这里只是修改了处理的api，实际处理还是security
            .loginProcessingUrl("/loginDeal")
            // 登录的用户名和密码参数名称
            .usernameParameter("username")
            .passwordParameter("pwd")
            .successHandler(new LoginSuccessHandler())
            .failureHandler(new LoginFailureHandler())
            .permitAll()
            .and().csrf().disable(); //关闭csrf防护

        // 将我们的repository设置到共享变量里
        http.setSharedObject(SecurityContextRepository.class, securityContextRepository);

        // 设置序列化
        http.exceptionHandling().authenticationEntryPoint(new CustomizeAuthenticationEntryPoint())
            .accessDeniedHandler(new CustomAccessDeniedHandler());

        http.addFilterBefore(customUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()
            .antMatchers("/test/*", "/login/**").permitAll()
            .antMatchers("/getData2").hasRole("dev")
            .antMatchers("/getData3").hasRole("admin")
            .antMatchers("/getData4").hasAnyRole("test1")
            .antMatchers("/getData5").hasAuthority("ROLE_test1")
            .antMatchers("/getData6").hasIpAddress("127.0.0.1")
            .antMatchers("/getData7").hasAnyAuthority("ROLE_test2", "ROLE_test3")
            .anyRequest().authenticated()
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
                    fsi.setPublishAuthorizationSuccess(true);
                    fsi.setAccessDecisionManager(new CustomAccessDecisionManager(cacheManager));
                    return fsi;
                }
            });

        //        http.authorizeRequests().anyRequest().access("@permissionService.hasPermission(this, 'read')");
    }
}
